package com.xiaomaoguai.fcp.pre.kepler.redis.lock.aop;

import com.xiaomaoguai.fcp.pre.kepler.redis.lock.LockException;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.service.LockService;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.anno.Lock;
import com.xiaomaoguai.fcp.pre.kepler.redis.lock.service.LockKeyBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * @fileName: LockAspect.java
 * @author: WeiHui
 * @date: 2018/8/30 19:03
 * @version: v1.0.0
 * @since JDK 1.8
 */
@Slf4j
@Aspect
@Order(1)
public class LockAspect {

	/**
	 * 锁服务
	 */
	@Resource
	private LockService lockService;

	@Resource
	private LockKeyBuilder springElLockKeyBuilder;

	@Resource
	private LockKeyBuilder aviatorLockKeyBuilder;

	@Around(value = "@annotation(com.xiaomaoguai.fcp.pre.kepler.redis.lock.anno.Lock)")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		boolean locked = false;
		String lockKey = null;
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			Method method = signature.getMethod();
			Lock lock = method.getAnnotation(Lock.class);
			lockKey = getKeyName(joinPoint, method, lock);
			log.info("当前锁key = [{}]", lockKey);
			locked = lockService.getLock(lockKey, 15);
			if (!locked) {
				throw new LockException("手速太快，请稍等哈！");
			}
			return joinPoint.proceed();
		} catch (Exception e) {
			throw e;
		} finally {
			if (locked && StringUtils.isNotBlank(lockKey)) {
				lockService.releaseLock(lockKey);
			}
		}
	}

	/**
	 * 获取锁key ,多个key值，采用 : 拼接
	 *
	 * @param joinPoint 切入点
	 * @param method    方法
	 * @param lock      锁注解
	 * @return 锁key
	 */
	private String getKeyName(ProceedingJoinPoint joinPoint, Method method, Lock lock) {
		String[] lockKeyEl = lock.lockKeyOfEl();
		String[] lockKeyOfAv = lock.lockKeyOfAv();
		if (ArrayUtils.isNotEmpty(lockKeyEl)) {
			return springElLockKeyBuilder.builder(lockKeyEl, method, joinPoint.getArgs());
		} else if (ArrayUtils.isNotEmpty(lockKeyOfAv)) {
			return aviatorLockKeyBuilder.builder(lockKeyOfAv, method, joinPoint.getArgs());
		} else {
			throw new LockException("分布式锁key未设置");
		}
	}

}
